﻿using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Jsl.StyleCop.CSharp.UnitTesting;
using StyleCop;
using StyleCop.CSharp;

namespace Jsl.StyleCop.CSharp
{
    [DebuggerStepThrough]
    [SourceAnalyzer(typeof(CsParser))]
    public class MethodCollectorRule : SourceAnalyzer
    {
        /// <summary>
        /// Rule name used by testing.
        /// </summary>
        public static readonly string MethodRule = "MethodRule";

        private static List<Method> methods;
        private static bool enableRule;

        #region Constructors
        public MethodCollectorRule()
        {
        }
        #endregion Constructors

        #region Public Properties
        public static bool EnableRule
        {
            get { return MethodCollectorRule.enableRule; }
            set { MethodCollectorRule.enableRule = value; }
        }

        public static List<Method> Methods
        {
            get { return MethodCollectorRule.methods; }
        }
        #endregion Public Properties

        #region Public Methods
        public static Expression GetExpression(string expressionCode)
        {
            return GetIfStatement("if (" + expressionCode + ") { }")
                .ConditionExpression;
        }

        public static IfStatement GetIfStatement(string ifCode)
        {
            var method = GetMethod(ifCode);
            return (IfStatement)method.ChildStatements.First();
        }

        public static Method GetMethod(string methodCode)
        {
            var code = @"public void Method()
{
" + methodCode + @"
}";
            return MethodCollectorRule.MethodFromMethodCode(code);
        }

        public static Method MethodFromSourceCode(string sourceCode)
        {
            return MethodCollectorRule.MethodsFromSourceCode(sourceCode).Single();
        }

        public static List<Method> MethodsFromSourceCode(string sourceCode)
        {
            MethodCollectorRule.EnableRule = true;
            try
            {
                // We will use the unit testing shadow directory to put our files in, etc.
                string testDirectory = Path.GetDirectoryName(typeof(MethodCollectorRule).Assembly.Location);

                // Create the source code file in the test directory.
                string sourceFileName = Path.Combine(testDirectory, "AnalysisTarget.cs");
                using (var writer = File.CreateText(sourceFileName))
                {
                    writer.Write(sourceCode);
                }

                // Build the list add-in paths. This is the directory the test assembly is in.
                List<string> addinPaths = new List<string>();
                addinPaths.Add(testDirectory);

                string settingsFileName = AnalysisHelper.BuildSettingsFile(
                    typeof(MethodCollectorRule),
                    null,
                    testDirectory);

                // Set up the source code analysis for the project.
                var console = new StyleCopConsole(settingsFileName, false, null, addinPaths, false);
                var configuration = new Configuration(new string[] { });
                var project = new CodeProject(sourceFileName.GetHashCode(), sourceFileName, configuration);

                // Add in the single source code file.
                console.Core.Environment.AddSourceCode(project, sourceFileName, null);

                // Process our single source code file.
                CodeProject[] projects = new CodeProject[] { project };
                console.Start(projects, true);

                return MethodCollectorRule.Methods;
            }
            finally
            {
                MethodCollectorRule.EnableRule = false;
            }
        }

        public static Method MethodFromMethodCode(string methodCode)
        {
            string sourceCode = "namespace MyNamespace { public class Class { " + methodCode + " } }";
            return MethodCollectorRule.MethodFromSourceCode(sourceCode);
        }

        public override void AnalyzeDocument(CodeDocument document)
        {
            if (!MethodCollectorRule.EnableRule)
            {
                return;
            }

            MethodCollectorRule.methods = new List<Method>();

            CsDocument csharpDocument = (CsDocument)document;
            csharpDocument.WalkDocument(this.CollectMethods);
        }

        public override bool IsRuleEnabled(CodeDocument document, string ruleName)
        {
            return MethodCollectorRule.enableRule;
        }
        #endregion Public Methods

        #region Private Methods
        private bool CollectMethods(CsElement element, CsElement parentElement, object context)
        {
            Method method = element as Method;
            if (method != null)
            {
                MethodCollectorRule.methods.Add(method);
            }

            return true;
        }
        #endregion Private Methods
    }
}